/*
 * Copyright 2009-2010 the original author or authors.
 *
 * Licensed under the Apache license, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.internna.iwebjtracker.model;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.directwebremoting.annotations.DataTransferObject;
import org.directwebremoting.annotations.RemoteProperty;
import org.hibernate.validator.Min;
import org.hibernate.validator.NotNull;
import org.internna.iwebjtracker.project.model.IWebJTrackerUser;
import org.internna.iwebmvc.metadata.ui.GridColumn;
import org.internna.iwebmvc.model.AbstractDomainEntity;
import org.internna.iwebmvc.model.Country;
import org.internna.iwebmvc.model.Role;
import org.internna.iwebmvc.model.Sex;
import org.internna.iwebmvc.model.security.GuestUser;
import org.internna.iwebmvc.model.security.UserImpl;
import org.internna.iwebmvc.utils.CollectionUtils;
import org.internna.iwebmvc.utils.StringUtils;

import static org.internna.iwebmvc.utils.StringUtils.hasText;

/**
 * A user in the application.
 *
 * @author Jose Noheda
 * @since 1.0
 */
@Entity
@Table(name = "TRACKER_USERS")
@NamedQueries({
    @NamedQuery(name = "TrackerUser.findAll", query = "SELECT u FROM TrackerUser u"),
    @NamedQuery(name = "TrackerUser.findByUsername", query = "SELECT u FROM TrackerUser u WHERE u.securityUser.username = :username")
})

@DataTransferObject(javascript = "TrackerUser")
public class TrackerUser extends AbstractDomainEntity implements IWebJTrackerUser {

    private static final long serialVersionUID = 4164821193424578112L;

    public static final String FIND_BY_USERNAME = "TrackerUser.findByUsername";

    @NotNull
    @OneToOne
    @GridColumn
    @RemoteProperty
    @JoinColumn(name = "USERNAME")
    private UserImpl securityUser;

    @GridColumn
    @RemoteProperty
    @JoinColumn(name = "SEX")
    @ManyToOne(fetch = FetchType.EAGER)
    private Sex sex;

    @GridColumn
    @RemoteProperty
    @JoinColumn(name = "LOCATION")
    @ManyToOne(fetch = FetchType.EAGER)
    private Country location;

    @ManyToMany(mappedBy = "developers")
    private Set<DefaultProject> memberships;

    @ManyToMany(mappedBy = "observers")
    private Set<DefaultProject> trackings;

    @Min(0)
    @RemoteProperty
    @Column(name = "REPUTATION")
    private int reputation = 0;

    /**
     * Accessor method
     *
     * @return a non null non modifiable collection
     */
    public Set<DefaultProject> getMemberships() {
        return CollectionUtils.isNotEmpty(memberships) ? Collections.unmodifiableSet(memberships) : Collections.EMPTY_SET;
    }

    /**
     * Adds one project to the group of memberships.
     *
     * @param project any
     */
    public final void addMembership(DefaultProject project) {
        if (project != null) {
            if (memberships == null) {
                memberships = new HashSet<DefaultProject>();
            }
            memberships.add(project);
        }
    }

    /**
     * Accessor method.
     *
     * @return a non null non modifiable collection
     */
    public Set<DefaultProject> getTrackings() {
        return CollectionUtils.isNotEmpty(trackings) ? Collections.unmodifiableSet(trackings) : Collections.EMPTY_SET;
    }

    /**
     * Accessor method.
     *
     * @param trackings any
     */
    public void setTrackings(Set<DefaultProject> trackings) {
        this.trackings = trackings;
    }

    /**
     * Adds one project to the group of tracked projects.
     *
     * @param project any
     */
    public final void addTracking(DefaultProject project) {
        if (project != null) {
            if (trackings == null) {
                trackings = new HashSet<DefaultProject>();
            }
            trackings.add(project);
        }
    }

    /**
     * Accessor method
     */
    public void setMemberships(Set<DefaultProject> memberships) {
        this.memberships = memberships;
    }

    /**
     * Accessor method
     */
    public UserImpl getSecurityUser() {
        return securityUser;
    }

    /**
     * Accessor method
     */
    public void setSecurityUser(UserImpl securityUser) {
        this.securityUser = securityUser;
    }

    /**
     * Accessor method
     */
    public Sex getSex() {
        return sex;
    }

    /**
     * Accessor method
     */
    public void setSex(Sex sex) {
        this.sex = sex;
    }

    /**
     * Accessor method
     */
    public Country getLocation() {
        return location;
    }

    /**
     * Accessor method
     */
    public void setLocation(Country location) {
        this.location = location;
    }

    /**
     * Accessor method
     */
    public int getReputation() {
        return reputation;
    }

    /**
     * Accessor method
     */
    protected void setReputation(int reputation) {
        this.reputation = reputation;
    }

    /**
     * Adds more reputation. Negative numbers are ommitted.
     *
     * @param increment any
     */
    @Override public void increaseReputation(int increment) {
        if (increment > 0) {
            reputation += increment;
        }
    }

    /**
     * Generates a gravatar URL using the email MD5 hash
     */
    @RemoteProperty public final String getGravatar() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        String mail = getEmail();
        return hasText(mail) ? "http://www.gravatar.com/avatar/" + StringUtils.MD5(mail) + ".jpg" : "http://www.gravatar.com/avatar/3b3be63a4c2a439b013787725dfce802.jpg";
    }

    /**
     * Delegates to getName()
     */
    @Override public final String getCaption(Locale locale) {
        return getName();
    }

    /**
     * Delegates to securityUser.setEmail()
     *
     * @param email any
     */
    @Override public final void setEmail(String email) {
        if (securityUser != null) {
            securityUser.setEmail(email);
        }
    }

    /**
     * Delegates to securityUser.getEmail()
     */
    @Override public final String getEmail() {
        return securityUser != null ? securityUser.getEmail() : null;
    }

    /**
     * Delegates to securityUser.setName()
     *
     * @param name any
     */
    @Override public final void setName(String name) {
        if (securityUser != null) {
            securityUser.setName(name);
        }
    }

    /**
     * Delegates to securityUser.setLocale()
     *
     * @param locale any
     */
    @Override public final void setLocale(Locale locale) {
        if (securityUser != null) {
            securityUser.setLocale(locale);
        }
    }

    /**
     * Delegates to securityUser.getLocale()
     */
    @Override public final Locale getLocale() {
        return securityUser != null ? securityUser.getLocale() : null;
    }

    /**
     * Delegates to securityUser.setTheme()
     *
     * @param theme any
     */
    @Override public final void setTheme(String theme) {
        if (securityUser != null) {
            securityUser.setTheme(theme);
        }
    }

    /**
     * Delegates to securityUser.getTheme(). Otherwise considers the user to be anonymous.
     */
    @Override public final String getTheme() {
        return securityUser != null ? securityUser.getTheme() : GuestUser.GUEST_THEME;
    }

    /**
     * Delegates to securityUser.isAnonymous() if able. Otherwise considers the user to be anonymous.
     */
    @Override public final boolean isAnonymous() {
        return securityUser != null ? securityUser.isAnonymous() : true;
    }

    /**
     * Delegates to securityUser.getUsername() if able.
     */
    @Override public final String getUsername() {
        return securityUser != null ? securityUser.getUsername() : null;
    }

    /**
     * Delegates to securityUser.getUsername() if able. Otherwise considers the user to be anonymous.
     */
    @Override public final Set<? extends Role> getRoles() {
        return securityUser != null ? securityUser.getRoles() : Collections.EMPTY_SET;
    }

    /**
     * Delegates to securityUser.clearRoles() if able.
     */
    @Override public final void clearRoles() {
        if (securityUser != null) {
            securityUser.clearRoles();
        }
    }

    /**
     * Delegates to securityUser.addRole() if able.
     */
    @Override public final void addRole(Role role) {
        if (securityUser != null) {
            securityUser.addRole(role);
        }
    }

    /**
     * Delegates to securityUser.toggle() if able.
     */
    @Override public final void toggle() {
        if (securityUser != null) {
            securityUser.toggle();
        }
    }

    /**
     * Delegates to securityUser.getName() if able. Otherwise considers the user to be anonymous.
     */
    @RemoteProperty
    @Override public final String getName() {
        return securityUser != null ? securityUser.getName() : GuestUser.GUEST_USER;
    }

    /**
     * Not implemented by design.
     *
     * @return null
     */
    @Override public String getPassword() {
        return null;
    }

}
